<?php

declare(strict_types=1);

namespace Trois\Lib;


use App\Model\AreaModel;
use EasySwoole\EasySwoole\Core;
use EasySwoole\Log\Logger;
use EasySwoole\ORM\DbManager;
use EasySwoole\Utility\Str;

class Help
{

    public static function userPassMd5(string $password, string $salt = ''): string
    {
        return md5(bin2hex(sha1($password, true)) . $salt);
    }

    /**
     * 获取毫秒级时间戳
     * @return float
     */
    public static function getMillisecond(): float
    {

        list($t1, $t2) = explode(' ', microtime());

        return (float)sprintf('%.0f', (floatval($t1) + floatval($t2)) * 1000);

    }

    /**
     * 手机号隐藏中间4位 ****
     * @param string $num
     * @return string
     */
    public static function phoneYc(string $num = ''): string
    {
        return $num ? substr_replace($num, '****', 3, 4) : '';
    }


    public static function classBasename($class): string
    {
        $class = is_object($class) ? get_class($class) : $class;

        return basename(str_replace('\\', '/', $class));
    }

    /**
     * 数据集的key作为索命
     */

    public static function keyAsIndex(array $res, string $key = 'id'): array
    {

        $key = trim($key);
        $keys = explode(',', $key);

        $results = [];

        if (count($keys) == 1) {

            $k = $keys[0];

            foreach ($res as $re) {
                $results[$re[$k]] = $re;
            }

        } else {

            foreach ($res as $re) {

                $_keys = [];
                foreach ($keys as $k) {
                    $_keys[] = $re[trim($k)];
                }

                $results[implode('-', $_keys)] = $re;

            }

        }


        return $results;
    }

    /**
     * 获取数组中的值
     * @param array $array
     * @param string $key
     * @param string $field
     * @param string $default
     * @return mixed
     */
    public static function getArrayValue(array $array, string $key, $field = '', $default = '-')
    {
        $key = trim($key);

        if ($key == '') {
            return $default;
        }

        return isset($array[$key])
            ? (($field == '')
                ? $array[$key]
                : ($array[$key][$field] ?? $default)
            )
            : $default;

    }

    /**
     * 模型 Invoke
     * @param string $model
     * @param \Closure $cb
     * @param bool $toArray
     * @param bool $notNul
     * @param bool $strict
     * @return mixed|void
     * @throws \EasySwoole\ORM\Exception\Exception
     * @throws \EasySwoole\Pool\Exception\PoolEmpty
     * @throws \Throwable
     */
    public static function modelInvokeBak(string $model, \Closure $cb, bool $toArray = true, bool $notNul = false, bool $strict = false)
    {
        return \EasySwoole\ORM\DbManager::getInstance()->invoke(function ($client) use ($model, $cb, $toArray, $notNul, $strict) {

            $data = call_user_func($cb, $model::invoke($client));

            if ($toArray) {
                return $data ? $data->toArray($notNul, $strict) : [];
            } else {
                return $data;
            }

        });

    }

    /**
     * 模型 Invoke
     * @param string $model
     * @param \Closure $cb
     * @param bool $toArray
     * @param bool $notNul
     * @param bool $strict
     * @return mixed|void
     * @throws \EasySwoole\ORM\Exception\Exception
     * @throws \EasySwoole\Pool\Exception\PoolEmpty
     * @throws \Throwable
     */
    public static function modelInvoke(string $model, \Closure $cb, bool $toArray = true, bool $notNul = false, bool $strict = false)
    {

        $model = new $model();

        $data = call_user_func($cb, $model);

//        dd($data);

        if ($toArray) {
            return $data ? $data->toArray($notNul, $strict) : [];
        } else {
            return $data;
        }

    }

    public static function redisPoolStringInvoke(string $key, $value = "", $timeout = 2 * 86400)
    {
        if ($value === '') {
            return \EasySwoole\RedisPool\RedisPool::invoke(function (\EasySwoole\Redis\Redis $redis) use ($key) {
                return $redis->get($key);
            });
        } else {

            return \EasySwoole\RedisPool\RedisPool::invoke(function (\EasySwoole\Redis\Redis $redis) use ($key, $value, $timeout) {
                return $redis->set($key, $value, $timeout);
            });
        }

    }

    public static function redisPoolStringDelInvoke(string $key)
    {

        return \EasySwoole\RedisPool\RedisPool::invoke(function (\EasySwoole\Redis\Redis $redis) use ($key) {
            return $redis->del($key);
        });

    }

    public static function redisPoolStringExpire(string $key, $timeout = 2 * 86400)
    {
        if ($key) {
            return \EasySwoole\RedisPool\RedisPool::invoke(function (\EasySwoole\Redis\Redis $redis) use ($key, $timeout) {
                return $redis->expire($key, $timeout);
            });
        }

    }

    public static function encrypt($txt): string
    {
        return PassTool::encrypt(json_encode($txt));
    }

    public static function decrypt(string $txt)
    {
        return json_decode(PassTool::decrypt($txt), true);
    }

    public static function getLastSql(): void
    {
        \EasySwoole\EasySwoole\Logger::getInstance()->info('SQL: ' . DbManager::getInstance()->getLastQuery()->getLastQuery());
    }

    /**
     * 测试环境时执行回调
     * @param \Closure $cb
     * @return false|mixed|void
     */
    public static function dev(\Closure $cb)
    {
        if (Core::getInstance()->runMode() === 'dev') {
            return call_user_func($cb);
        }
    }

    /**
     * 将字符串参数变为数组
     * @param $query
     * @return array
     */
    public static function convertUrlQuery($query): array
    {

        $queryParts = explode('&', $query);
        $params = [];

        foreach ($queryParts as $param) {

            $item = explode('=', $param);

            $params[$item[0]] = $item[1];
        }

        return $params;

    }

    public static function getAllCity(): array
    {

        $citys = Help::redisPoolStringInvoke('system:area_city');

        $citys = $citys ? json_decode($citys, true) : [];

        if (!$citys) {

            $areas = [];

            $res = Help::modelInvoke(AreaModel::class, function ($model) {
                return $model->field('area_id,area_name,area_sn,area_parent_sn')->all();
            });

            foreach ($res as $re) {
                if ($re['area_parent_sn'] == 0) {
                    $re['city_list'] = [];
                    $areas[$re['area_sn']] = $re;
                }
            }

            foreach ($res as $re) {
                isset($areas[$re['area_parent_sn']]) && $areas[$re['area_parent_sn']]['city_list'][$re['area_id']] = $re;
            }

            $citys = [];

            foreach ($areas as $pros) {

                foreach ($pros['city_list'] as $city) {

                    $_city = mb_substr($city['area_name'], 0, 2, 'utf-8');

                    $citys[$_city] = [
                        'sn' => $city['area_sn'],           //市SN
                        'na' => $city['area_name'],         //市名称
                        'psn' => $city['area_parent_sn'], //省SN
                        'pna' => $pros['area_name'],      //省名称
                    ];

                }

            }

            Help::redisPoolStringInvoke('system:area_city', json_encode($citys, JSON_UNESCAPED_UNICODE), 0);
        }

        return $citys;
    }

    public static function getCity(string $name): array
    {
        /**
         * return Array
         * (
         * [sn] => 712200
         * [name] => 宜兰县
         * [p_sn] => 710000
         * [p_name] => 台湾
         * )
         */
        $citys = Help::getAllCity();
        return $citys[trim($name)] ?? [];
    }

    public static function getIp138(string $ip): array
    {
        if ($ip == '127.0.0.1' || Str::startsWith($ip, '192.168')) {
            return [
                'success' => 0,
                'message' => 'Internal Service Error:ip[' . $ip . '] loc failed',
                'country' => '',
                'region' => '',
                'city' => '',
                'district' => '',
                'zip' => '',
                'zone' => '',
            ];
        }

        $response = Help::curl('get', 'http://api.ip138.com/query/', ['ip' => $ip, 'datatype' => 'json'], ['token:767e6aa5f36d0ee80c42a46d92f8171d'], 3);

        if ($response['response_code'] === 0) {

            if ($response['response_content']['ret'] === 'ok') {
                return [
                    'success' => 1,
                    'message' => 'success',
                    'country' => $response['response_content']['data'][0] ?? '', // 国家（极少为空）
                    'region' => $response['response_content']['data'][1] ?? '', // 省份/自治区/直辖市（少数为空）
                    'city' => $response['response_content']['data'][2] ?? '',   // 地级市（部份为空）
                    'district' => $response['response_content']['data'][3] ?? '',  // 区/县（部份为空）
                    'zip' => $response['response_content']['data'][4] ?? 0, // 邮政编码
                    'zone' => $response['response_content']['data'][5] ?? 0,   // 地区区号
                ];
            } else {
                return [
                    'success' => 0,
                    'message' => $response['response_content']['msg'],
                    'country' => '',
                    'region' => '',
                    'city' => '',
                    'district' => '',
                    'zip' => '',
                    'zone' => '',
                ];
            }
        }

        return [
            'success' => 0,
            'message' => $response['response_message'],
            'country' => '',
            'region' => '',
            'city' => '',
            'district' => '',
            'zip' => '',
            'zone' => '',
        ];

    }

    public static function curl(string $method = 'get', string $url, $send_data = [], array $header = [], int $timeout = 120): array
    {
        try {
            //启动一个CURL会话
            $ch = curl_init();
            if (strtolower($method) == 'get') {
                if ($send_data) {
                    $send_data = http_build_query($send_data);
                    if (!empty($send_data)) $url .= '?' . $send_data;
                }
            }

            // 设置curl允许执行的最长秒数
            curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            // 获取的信息以文件流的形式返回，而不是直接输出。
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            //设置请求地址
            curl_setopt($ch, CURLOPT_URL, trim($url));
            curl_setopt($ch, CURLOPT_HEADER, TRUE);

            if (strtolower($method) == 'post') {
                curl_setopt($ch, CURLOPT_POST, TRUE);
                curl_setopt($ch, CURLOPT_POSTFIELDS, $send_data);
            }

            if ($header) {
                curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
            }

            try {
                $content = curl_exec($ch);

                $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);

                $body = substr($content, $headerSize);

            } catch (\Exception $e) {
                logger_error($e->getMessage(), 'curl请求错误');
                logger_error($url, 'url地址');
                logger_error($method, 'url-methond');
                logger_error(json_encode($header), 'url-header');
                logger_error(json_encode($send_data), 'url-send_data');
            } catch (\Throwable $e) {
                logger_error($e->getMessage(), 'curl请求错误');
                logger_error($url, 'url地址');
                logger_error($method, 'url-methond');
                logger_error(json_encode($header), 'url-header');
                logger_error(json_encode($send_data), 'url-send_data');
            }

            curl_close($ch);

            $content = @json_decode($body, true);

            if ($content) {
                return [
                    'response_code' => 0,
                    'response_message' => '接口请求成功',
                    'response_content' => $content
                ];
            }

            return [
                'response_code' => -1,
                'response_message' => '接口请求失败',
                'response_content' => []
            ];

        } catch (\Exception $e) {

            return [
                'response_code' => -1,
                'response_message' => '接口请求失败，错误信息：' . $e->getMessage(),
                'response_content' => []
            ];

        }

    }

    /**
     * xml转换成数组
     */
    public static function xml2Array(string $xml): array
    {
        $obj = null;
        if ($xml) {
            $obj = simplexml_load_string($xml, "SimpleXMLElement", LIBXML_NOCDATA);
        }
        return (array)$obj;
    }

    /**
     * Redis 加锁
     */
    public static function redisLock(string $sessID, int $timeout = 10): bool
    {
        $key = 'LOCK_PREFIX_' . $sessID;

        return \EasySwoole\RedisPool\RedisPool::invoke(function (\EasySwoole\Redis\Redis $redis) use ($key, $timeout) {
            $isLock = $redis->setNx($key, 1);
            if ($isLock) {
                // 没加过锁, 设置过期时间，防止死任务的出现
                $redis->expire($key, $timeout);
                return true;
            }

            //已加过锁
            return false;
        });

    }

    /**
     * Redis 解锁
     */
    public static function redisUnlock(string $sessID): void
    {
        $key = 'LOCK_PREFIX_' . $sessID;

        \EasySwoole\RedisPool\RedisPool::invoke(function (\EasySwoole\Redis\Redis $redis) use ($key) {
            $redis->del($key);
        });
    }


    /**
     * 获取系统时间
     */
    public static function getMicrotime(): float
    {
        list($usec, $sec) = explode(' ', microtime()); //取系统时间 前半部分是时间戳，后半部分是微妙部分
        return ((float)$usec + (float)$sec);
    }

    /**
     * 下载文件
     */
    public static function downloadFile(string $fileUrl, string $saveTo, $mode = LOCK_EX): bool
    {
        $content = file_get_contents($fileUrl);
        return (bool)file_put_contents($saveTo, $content, $mode);
    }

    /**
     * 毫秒级时间戳
     * @return float
     */
    public static function getMillisecondTimeStamp(): float
    {
        list($mSec, $sec) = explode(' ', microtime());
        return (float)sprintf('%.0f', (floatval($mSec) + floatval($sec)) * 1000);
    }

}
